#include "ChatPanel.h"
#include "../../core/Impp.h"
#include "../StdUserInterfaceModule/Icons.h"
#include <wx/window.h>
#include <wx/accel.h>
#include "../../core/database/HistorySession.h"
#include <wx/regex.h>
#include <wx/colour.h>
#include "../StdUserInterfaceModule/ImppBitmapButton.h"

namespace Impp
{

BEGIN_EVENT_TABLE(ChatPanel, wxPanel)
	EVT_TEXT(ID_INPUT, ChatPanel::onInputChanged)
	EVT_BUTTON(ID_SEND,ChatPanel::onSend)
	EVT_BUTTON(ID_CTRL_ENTER,ChatPanel::onCtrlEnter)
	EVT_TEXT_URL(ID_LOG, ChatPanel::OnURL)
	EVT_TIMER(ID_TYPING_TIMER, ChatPanel::onTextTyped)
END_EVENT_TABLE()

ChatPanel::ChatPanel(ChatWindow* cw, wxNotebook* nb, Buddy* buddy) :
	wxPanel(nb, ID_CHATPANE,wxDefaultPosition,wxDefaultSize,wxBORDER_NONE),buddy(buddy),cw(cw),nb(nb),lastIncomingMsgHistoryId(0),
	typingNotificationSent(false),lastMessageSent(0)
{


	typingNotifyTimer = new wxTimer(this,ID_TYPING_TIMER);

	//wxText
	wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
	wxBoxSizer* infoSizer = new wxBoxSizer(wxHORIZONTAL);
	wxBoxSizer* infoRightSizer = new wxBoxSizer(wxVERTICAL);
	wxBoxSizer* infoTopSizer = new wxBoxSizer(wxHORIZONTAL);
	buttonSizer = new wxBoxSizer(wxHORIZONTAL);

	wxPanel* info = new wxPanel(this);
	wxPanel* infoRight = new wxPanel(info);
	wxPanel* infoTop = new wxPanel(infoRight);

	//left
	avatarArea = new AvatarArea(info,wxNewId(),buddy, wxSize(32,32));

	//right - top
	wxStaticText* name = new wxStaticText(infoTop,wxNewId(),buddy->getCustomName() << wxT(" (") << buddy->getScreenName() << wxT(")"));
	typingStateText = new wxStaticText(infoTop,wxNewId(),wxT("..."));

	//right - bottom
	wxString statusMsg = buddy->getStatusMsg();
	if(statusMsg.IsEmpty()){
		statusMsg = statusToString(buddy->getStatus());
	}
	statusMessageText = new wxStaticText(infoRight, wxNewId(),statusMsg,wxDefaultPosition,wxSize(wxDefaultCoord, 20));

	infoSizer->Add(avatarArea,0,wxSHAPED|wxTOP|wxBOTTOM|wxRIGHT,4);
	infoSizer->Add(infoRight,1,wxEXPAND|wxALL,0);

	infoRightSizer->Add(infoTop,1,wxEXPAND|wxALL,0);
	infoRightSizer->Add(statusMessageText,0,wxEXPAND|wxALL|wxALIGN_LEFT,0);

	infoTopSizer->Add(name,0,wxEXPAND|wxALL,0);
	infoTopSizer->Add(typingStateText,1,wxEXPAND|wxALL|wxALIGN_LEFT,0);


	info->SetSizer(infoSizer);
	infoRight->SetSizer(infoRightSizer);
	infoTop->SetSizer(infoTopSizer);


	splitView = new wxSplitterWindow(this,wxID_ANY,wxDefaultPosition,wxDefaultSize,wxSP_LIVE_UPDATE|wxSP_3D);


	ModuleManager* modules = Impp::exemplar()->getModuleManager();
	modules->loadAndInit(MODULE_INTERFACE_CHATLOG);

	iModule* module = modules->getModuleByInterface(MODULE_INTERFACE_CHATLOG);
	if(module != NULL){
		if(iChatLogFactory* logFactory = dynamic_cast<iChatLogFactory*>(module)){
			log = logFactory->newChatLog(buddy, splitView, ID_LOG);
		}else{
			wxASSERT(logFactory != NULL);
		}
	}else{
		wxLogError(wxT("Coudn't create ChatPanel, no Chatlog Module found"));
		wxASSERT(module != NULL);
	}

	wxPanel* inputPane = new wxPanel(splitView,wxID_ANY);
	wxBoxSizer* inputPaneSizer = new wxBoxSizer(wxVERTICAL);


	iUserInterface* ui = Impp::exemplar()->getModuleManager()->getUserInterfaceModule();
	wxBitmap sendBitmap;
	if(ui != NULL){
		sendBitmap = ui->getIcons()->getBitmap(wxT("icons/chat/send.png"));
	}

	buttonPane = new wxPanel(inputPane,wxID_ANY);
	sendButton = new ImppBitmapButton(buttonPane,ID_SEND,sendBitmap,wxDefaultPosition,wxSize(24,24));
	//A dummy button f�r strg enter (nl)
	wxButton* nlButton = new wxButton(buttonPane,ID_CTRL_ENTER,wxT("ctrl-Enter"));
	nlButton->Hide();

	input = new wxRichTextCtrl(inputPane,ID_INPUT,wxEmptyString,wxDefaultPosition,wxDefaultSize,wxRE_MULTILINE|wxWANTS_CHARS|wxTE_PROCESS_TAB);
	input->Connect(wxEVT_CHAR, wxKeyEventHandler(ChatPanel::onInputKey),NULL, this);


	//wxButton* historybt = new wxBitmapButton(buttonPane,ID_BUTTON_HISTORY,Icons::buttonHistory);
	//wxButton* userinfoBt = new wxBitmapButton(buttonPane,ID_BUTTON_USERINFO,Icons::buttonUserinfo);


	std::vector<ButtonDefination*> buttons = cw->getButtonList()->getButtons();

	std::vector<ButtonDefination*>::iterator b;
	for(b = buttons.begin(); b != buttons.end(); b++){
		ButtonDefination* button = *b;

		if(ActionButtonDefination* actionButton = dynamic_cast<ActionButtonDefination*>(button)){
			wxBitmap bitmap;

			if(ui != NULL){
				bitmap = ui->getIcons()->getActionIcon(actionButton->moduleName, actionButton->actionName);
			}

			ImppBitmapButton* bt = new ImppBitmapButton(buttonPane,actionButton->id,bitmap,wxDefaultPosition,wxSize(24,24));
			bt->setNoPadding();

			buttonSizer->Add(bt,0,wxSHAPED|wxALIGN_LEFT);
			Connect(button->id, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(ChatPanel::onButton), NULL, this);
		}

	}

	//buttonSizer->Add(historybt,0,wxSHAPED|wxALIGN_LEFT);
	//buttonSizer->Add(userinfoBt,0,wxSHAPED|wxALIGN_LEFT);
	buttonSizer->Add(nlButton,1,wxSHAPED|wxALIGN_RIGHT);
	buttonSizer->Add(sendButton,1,wxSHAPED|wxALIGN_RIGHT);

	buttonPane->SetSizer(buttonSizer);

	inputPaneSizer->Add(buttonPane,0,wxEXPAND);
	inputPaneSizer->Add(input,1,wxEXPAND);
	inputPane->SetSizer(inputPaneSizer);
	inputPane->Layout();

	sizer->Add(info,0,wxTOP|wxLEFT|wxRIGHT|wxGROW,5);
    sizer->Add(splitView,1,wxBOTTOM|wxLEFT|wxRIGHT|wxGROW,5);

	splitView->SplitHorizontally(log->getWindow(),inputPane, -80);
	splitView->SetMinimumPaneSize(20);
	splitView->SetSashGravity(1.0);


    SetSizer(sizer);
    Layout();

    sendButton->Disable();
    sendButton->SetDefault();


    wxAcceleratorEntry entries[1];
    entries[0].Set(wxACCEL_CTRL,  WXK_RETURN, ID_CTRL_ENTER);
    wxAcceleratorTable accel(1, entries);
    SetAcceleratorTable(accel);


    Buddy* self = Impp::exemplar()->getSelf(buddy->getAccountId());
    HistorySession* curSession = Impp::exemplar()->getDatabase()->getCurrentHistorySession(buddy);

    if(curSession!=NULL){
    	log->freeze();
    	std::vector<HistoryEntry*> history;
    	curSession->getHistoryEntries(history);

    	std::vector<HistoryEntry*>::iterator dIter(history.begin());
    	for (; dIter != history.end(); ++dIter) {
    		HistoryEntry* e = *dIter;
    		log->writeMessage(e->getTime(),e->isOutgoing() ? self : e->getBuddy(),e->getMsg(),!e->isOutgoing(), true);
    		if(!e->isOutgoing()){
    			lastIncomingMsgHistoryId = e->getId();
    		}
    		delete e;
    	}
    	log->thaw();
    	log->scrollToEnd();

    }

    statusMsg = buddy->getStatusMsg();
    if(!statusMsg.IsEmpty()){
    	log->writeStatusMessage(buddy->getStatus(),statusMsg);
    	log->scrollToEnd();
    }
}

void ChatPanel::addButton(const wxString & name, wxBitmap* icon, wxWindowID id){

	wxButton* bt = new wxBitmapButton(buttonPane,id,*icon);
	buttonSizer->Add(bt,1,wxSHAPED|wxALIGN_LEFT);
}

ChatPanel::~ChatPanel()
{
	wxDELETE(log);
	wxDELETE(typingNotifyTimer);
}

void ChatPanel::initPane(){
	splitView->SetSashPosition(-80);
	input->SetFocus();
}

void ChatPanel::onInputKey(wxKeyEvent & event){
	//wxLogDebug(wxT("KeyEvent"));

	if(event.GetKeyCode() != WXK_RETURN){ //not return pressed -> nothing to do
		event.Skip(true);
		return;
	}

	if(event.GetKeyCode() == WXK_RETURN && event.ControlDown()){ //return and ctrl pressed -> nothing to do
		event.Skip(true);
		return;
	}

	doSend();

}

bool ChatPanel::sendTypingNotifications(){
	return !(wxDateTime::GetTimeNow()-lastMessageSent > 60*30 ); //older then 30 minutes
}

void ChatPanel::onInputChanged(wxCommandEvent & event){


	bool sendTypingNotify = sendTypingNotifications();

	if(input->IsEmpty()){
		//wxLogDebug(wxT("Typing finished"));
		if(sendTypingNotify){
			Impp::exemplar()->sendTypingNotification(buddy, TYPING_FINISHED);
		}


		typingNotifyTimer->Stop();
		typingNotificationSent=false;

		if(sendButton->IsEnabled()){
			sendButton->Disable();
		}
	}else{
		if(!sendButton->IsEnabled()){
			sendButton->Enable();
		}
		if(!typingNotificationSent){
			//wxLogDebug(wxT("typing started"));

			if(sendTypingNotify){
				Impp::exemplar()->sendTypingNotification(buddy, TYPING_STARTED);
			}
		}

		typingNotifyTimer->Start(2000,wxTIMER_ONE_SHOT);
		typingNotificationSent=true;
	}
}

void ChatPanel::onTextTyped(wxTimerEvent & event){

	//wxLogDebug(wxT("text typed"));

	if(sendTypingNotifications()){
		Impp::exemplar()->sendTypingNotification(buddy, TYPING_TEXT_TYPED);
	}
	typingNotificationSent=false;
}

void ChatPanel::onSend(wxCommandEvent & event){
	doSend();
}


void ChatPanel::onButton(wxCommandEvent & event){
	int id = event.GetId();
	std::vector<ButtonDefination*> buttons = cw->getButtonList()->getButtons();

	std::vector<ButtonDefination*>::iterator b;
	for(b = buttons.begin(); b != buttons.end(); b++){
		ButtonDefination* button = *b;

		if(ActionButtonDefination* actionButton = dynamic_cast<ActionButtonDefination*>(button)){
			if(actionButton->id == id){
				Impp::exemplar()->processCommandAction(actionButton->moduleName, actionButton->actionName, true, buddy);
				return;
			}
		}

	}
	wxLogDebug(wxT("ChatPanel::onButton: Button not found!"));

}

void ChatPanel::messageIn(MessageEvent & event){
	db_id historyId = event.getHistoryId();
	if(historyId!=lastIncomingMsgHistoryId){
		log->writeMessage(event.getTime(),event.getBuddy(),event.getMessage(),true, false);
		lastIncomingMsgHistoryId = historyId;
		log->scrollToEnd();
	}else{
		wxLogDebug(wxT("ChatPanel::messageIn: doubled msg not written"));
	}
}

void ChatPanel::doSend(){
	if(!input->IsEmpty()){
		//the msg
		wxString msg = input->GetValue();

		msg.Trim();

		ActionId id = Impp::exemplar()->sendMessage(buddy,msg);
		log->writeMessage(wxDateTime::Now(),Impp::exemplar()->getSelf(buddy->getAccountId()),msg,false,false,id);
		input->SetValue(wxEmptyString);

		log->scrollToEnd();

		lastMessageSent = wxDateTime::GetTimeNow();
	}
}

void ChatPanel::typingNotification(TypingNotificationEvent & event){
	TypingState state = event.getState();

	switch (state) {
		case TYPING_STARTED:
			typingStateText->SetLabel(wxT(" is typing."));
			break;
		case TYPING_TEXT_TYPED:
			typingStateText->SetLabel(wxT(" has typed."));
			break;
		case TYPING_FINISHED:
			typingStateText->SetLabel(wxT("..."));
			break;
		default:
			wxLogDebug(wxT("ChatPanel::typingNotification: shoud never happen"));
			break;
	}
}


void ChatPanel::messageCallback(MessageCallbackEvent & event){

	log->markMessage(event);

}
//for wxMSW
void ChatPanel::onCtrlEnter(wxCommandEvent & event){
	input->WriteText(wxT("\n"));
}


void ChatPanel::statusChanged(StatusEvent & event){
	wxString statusMsg = event.getStatusMsg();

	if(event.isStatusDifferent() || event.isMsgDifferent()){

		if(event.isMsgDifferent()){
			log->writeStatusMessage(event.getNewStatus(),statusMsg);
		}else{
			log->writeStatusMessage(event.getNewStatus(),wxEmptyString);
		}


	    log->scrollToEnd();
	}

	if(statusMsg.IsEmpty()){
		statusMsg = statusToString(event.getNewStatus());
	}

	statusMessageText->SetLabel(statusMsg);
}

void ChatPanel::avatarChanged(AvatarChangedEvent & event){
	avatarArea->avatarChanged();
}

void ChatPanel::IncomingFile(FileTransfareEvent & event){
	log->writeMessage(wxDateTime::Now(),event.getSender(), wxT("Incoming File Transfare"), true, false, -1);
}

Buddy* ChatPanel::getBuddy(){
	return buddy;
}

void ChatPanel::tabChanged(){
	input->SetFocus();
	clearUnreadMsgs();
}

void ChatPanel::clearUnreadMsgs(){
	Impp::exemplar()->processUnreadMessageRead(buddy);
}

void ChatPanel::OnURL(wxTextUrlEvent& event){
	::wxLaunchDefaultBrowser(event.GetString());
}

}
